package cn.org.mrliu.ssm.util.log4j;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Iterator;

import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;

public class JDBCAppender extends org.apache.log4j.jdbc.JDBCAppender {
	/**
	 * loops through the buffer of LoggingEvents, gets a sql string from
	 * getLogStatement() and sends it to execute(). Errors are sent to the
	 * errorHandler.
	 *
	 * If a statement fails the LoggingEvent stays in the buffer!
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void flushBuffer() {
		// Do the actual logging
		removes.ensureCapacity(buffer.size());
		for (Iterator i = buffer.iterator(); i.hasNext();) {
			LoggingEvent logEvent = (LoggingEvent) i.next();
			try {
				ThrowableInformation throwableInformation = logEvent.getThrowableInformation();
				if (throwableInformation != null) {
					Throwable throwable = throwableInformation.getThrowable();
					if (throwable != null) {
						System.out.println(throwable.getMessage());
						System.out.println(throwable.getLocalizedMessage());
						System.out.println(throwable.getCause());
					}
				}
				String sql = "INSERT INTO log4j(userId,createTime,logger,level,message) VALUES(?,?,?,?,?)";
				// String sql = getLogStatement(logEvent);
				execute(sql, 1, logEvent.getTimeStamp(), logEvent.getLoggerName(), logEvent.getLevel().toString(),
						logEvent.getRenderedMessage());
			} catch (SQLException e) {
				errorHandler.error("Failed to excute sql", e, ErrorCode.FLUSH_FAILURE);
			} finally {
				removes.add(logEvent);
			}
		}

		// remove from the buffer any events that were reported
		buffer.removeAll(removes);

		// clear the buffer of reported events
		removes.clear();
	}

	/**
	 *
	 * Override this to provide an alertnate method of getting connections (such
	 * as caching). One method to fix this is to open connections at the start
	 * of flushBuffer() and close them at the end. I use a connection pool
	 * outside of JDBCAppender which is accessed in an override of this method.
	 */
	protected void execute(String sql, Integer userId, long createTime, String logger, String level, String message)
			throws SQLException {

		Connection con = null;
		PreparedStatement stmt = null;

		try {
			con = getConnection();

			stmt = con.prepareStatement(sql);
			stmt.setInt(1, userId);
			stmt.setTimestamp(2, new Timestamp(createTime));
			stmt.setString(3, logger);
			stmt.setString(4, level);
			stmt.setString(5, message);
			stmt.executeUpdate();
		} finally {
			if (stmt != null) {
				stmt.close();
			}
			closeConnection(con);
		}

		// System.out.println("Execute: " + sql);
	}
}
